package com.shop.front.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.bt.rpc.model.RpcResult;
import com.github.yulichang.query.MPJQueryWrapper;
import com.shop.common.config.GlobalConfig;
import com.shop.common.entity.QuestionOption;
import com.shop.common.entity.course.CourseHistory;
import com.shop.common.entity.paper.Paper;
import com.shop.common.entity.paper.PaperAssign;
import com.shop.common.entity.paper.PaperLog;
import com.shop.common.entity.paper.PaperQuestion;
import com.shop.common.enums.CreditActionEnum;
import com.shop.common.enums.PaperAssignStatusEnum;
import com.shop.common.enums.QuestionTypeEnum;
import com.shop.common.exception.OperateException;
import com.shop.common.mapper.CategoryMapper;
import com.shop.common.mapper.CreditMapper;
import com.shop.common.mapper.QuestionMapper;
import com.shop.common.mapper.course.CourseHistoryMapper;
import com.shop.common.mapper.paper.*;
import com.shop.common.util.IpUtils;
import com.shop.common.util.ListUtils;
import com.shop.common.util.TimeUtils;
import com.shop.common.vo.PaperQuestionVo;
import com.shop.front.FrontThreadLocal;
import com.shop.front.service.ICourseService;
import com.shop.front.service.IPaperService;
import com.shop.front.validate.paper.SubmitValidate;
import com.shop.front.vo.course.CourseCategoryListedVo;
import com.shop.front.vo.paper.*;
import com.wy.tech.rpc.CollegeRpc;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.*;

@Service
public class PaperServiceImpl implements IPaperService {
    @Resource
    PaperMapper paperMapper;

    @Resource
    PaperCourseMapper paperCourseMapper;

    @Resource
    QuestionMapper questionMapper;

    @Resource
    PaperLogMapper paperLogMapper;

    @Resource
    PaperAssignMapper paperAssignMapper;

    @Resource
    PaperQuestionMapper paperQuestionMapper;
    
    @Resource
    CreditMapper creditMapper;

    @Resource
    CourseHistoryMapper courseHistoryMapper;

    @Resource
    CategoryMapper categoryMapper;

    @Resource
    ICourseService iCourseService;

    @Resource
    CollegeRpc collegeRpc;

    /**
     * 待考试列表
     *
     * @return
     */
    @Override
    public List<PaperListedVo> unTaken() {
        String userId = FrontThreadLocal.getUserId();

        QueryWrapper<Paper> queryWrapper = new QueryWrapper<>();
        String sql = "select paper_id from ?_paper_assign where user_id ='" + userId + "' and is_delete = 0 and status != 2 and status != 1";
        sql += String.format(" AND (days = 0 or (create_time + days * 86400) > %s)",  System.currentTimeMillis() / 1000);
        sql = sql.replace("?_", GlobalConfig.tablePrefix);
        queryWrapper.inSql("id", sql);
        queryWrapper.eq("status", 1);
        queryWrapper.eq("is_delete", 0);
        List<Paper> list = paperMapper.selectList(queryWrapper);
        List<PaperListedVo> voList = new ArrayList<>();
        for (Paper paper : list) {
            PaperListedVo vo = new PaperListedVo();
            BeanUtils.copyProperties(paper, vo);
            voList.add(vo);
        }
        return voList;
    }

    /**
     * 根据ID查询试卷详情
     */
    @Override
    public PaperDetailedVo detail(Integer paperId) {
        Paper paper = paperMapper.selectById(paperId);
        return _detail(paper);
    }

    /**
     * 根据codec查询详情
     */
    @Override
    public PaperDetailedVo detail(String code) {
        QueryWrapper<Paper> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("code", code);
        Paper paper = paperMapper.selectOne(queryWrapper);
        return _detail(paper);
    }

    /**
     * 试卷详情，不含题
     * @param paper
     * @return
     */
    private PaperDetailedVo _detail(Paper paper){
        PaperDetailedVo paperDetailedVo = _getPaperBaseDetail(paper);
        // 默认是已完成，后面查到了未完成的课程则更改为0标记为未完成
        paperDetailedVo.setFinished(1);

        // 查询关联课程
        MPJQueryWrapper<PaperCourseVo> paperCourseQueryWrapper = new MPJQueryWrapper<>();
        paperCourseQueryWrapper.eq("paper_id", paper.getId());
        paperCourseQueryWrapper.eq("t.is_delete", 0);
        paperCourseQueryWrapper.select("t.*,c.category_ids");
        paperCourseQueryWrapper.innerJoin("?_course as c on c.is_delete=0 and c.id = t.course_id".replace("?_", GlobalConfig.tablePrefix));
        List<PaperCourseVo> courseList = paperCourseMapper.selectJoinList(PaperCourseVo.class, paperCourseQueryWrapper);

        if(!courseList.isEmpty()){
            // 查询课程分类
            Map<Integer, CourseCategoryListedVo> categoryMap = iCourseService.categoryMap();

            // 提取课程ID
            List<Integer> courseIds = new ArrayList<>();
            for(PaperCourseVo paperCourse: courseList){
                courseIds.add(paperCourse.getCourseId());
                if(paperCourse.getCategoryIds() != null){
                    List<CourseCategoryListedVo> categories = new ArrayList<>();
                    for(Integer categoryId: ListUtils.stringToListAsInt(paperCourse.getCategoryIds(), ",")){
                        CourseCategoryListedVo category = categoryMap.get(categoryId);
                        if(category != null)categories.add(category);
                    }
                    paperCourse.setCategories(categories);
                }
            }
            // 查询完成了学习任务的课程
            List<CourseHistory> finishedList = courseHistoryMapper.selectList(new QueryWrapper<CourseHistory>()
                    .eq("user_id", FrontThreadLocal.getUserId())
                    .in("course_id", courseIds)
                    .eq("finished", 1));
            // 提取已完成的课程ID
            List<Integer> finishedCourseIds = new ArrayList<>();
            for(CourseHistory courseHistory: finishedList){
                finishedCourseIds.add(courseHistory.getCourseId());
            }
            for(PaperCourseVo paperCourse: courseList){
                if(finishedCourseIds.contains(paperCourse.getCourseId())){
                    paperCourse.setFinished(1);
                }else{
                    paperCourse.setFinished(0);
                    paperDetailedVo.setFinished(0);
                }
            }

        }
        paperDetailedVo.setCourses(courseList);
        return paperDetailedVo;
    }

    // 查询试卷基本信息，不含课程和题目
    private PaperDetailedVo _getPaperBaseDetail(Paper paper) {
        Assert.notNull(paper, "试卷不存在");
        // 查询当前用户的考试任务
        String userId = FrontThreadLocal.getUserId();
        QueryWrapper<PaperAssign> paperAssignQueryWrapper = new QueryWrapper<>();
        paperAssignQueryWrapper.eq("user_id", userId);
        paperAssignQueryWrapper.eq("paper_id", paper.getId());
        paperAssignQueryWrapper.eq("is_delete", 0);
        paperAssignQueryWrapper.ne("status", 2);
        paperAssignQueryWrapper.and(i -> i.eq("days", 0)
                .or().apply("create_time + days * 86400 > " + System.currentTimeMillis() / 1000));
        paperAssignQueryWrapper.last("limit 1");
        PaperAssign paperAssign = paperAssignMapper.selectOne(paperAssignQueryWrapper);
        Assert.notNull(paperAssign, "未找到考试任务");

        // 判断是否过期
        if(paperAssign.getDays() != null && paperAssign.getDays() > 0){
            long expireTime = paperAssign.getCreateTime() + paperAssign.getDays() * 86400;
            if (System.currentTimeMillis() / 1000 > expireTime) {
                throw new OperateException("考试任务已过期");
            }
        }

        PaperDetailedVo vo = new PaperDetailedVo();
        BeanUtils.copyProperties(paper, vo);
        vo.setPaperAssign(paperAssign);

        // 查询试题数
        QueryWrapper<PaperQuestion> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("is_delete", 0);
        queryWrapper.eq("paper_id", paper.getId());
        long questionCount = paperQuestionMapper.selectCount(queryWrapper);
        vo.setQuestionCount(Integer.parseInt(String.valueOf(questionCount)));
        return vo;
    }

    /**
     * 出题
     */
    @Override
    public PaperDetailedVo drawQuestions(Integer paperId) {
        PaperDetailedVo paperDetailedVo = detail(paperId);
        Assert.isTrue(paperDetailedVo.getFinished() == 1, "未完成前置课程学习任务");
        PaperAssign paperAssign = paperDetailedVo.getPaperAssign();
        Assert.notNull(paperAssign.getStatus() != PaperAssignStatusEnum.JUDGING.getCode() &&
                paperAssign.getStatus() != PaperAssignStatusEnum.PASSED.getCode(), "未找到考试任务");
        // 查询试题
        MPJQueryWrapper<PaperQuestionVo> queryWrapper = new MPJQueryWrapper<>();
        queryWrapper.select("t.*,pq.score");
        queryWrapper.eq("t.is_delete", 0);
        queryWrapper.eq("paper_id", paperDetailedVo.getId());
        queryWrapper.innerJoin("?_paper_question as pq on pq.is_delete=0 and pq.question_id = t.id".replace("?_", GlobalConfig.tablePrefix));
        List<PaperQuestionVo> questions = questionMapper.selectJoinList(PaperQuestionVo.class, queryWrapper);

        for (PaperQuestionVo question : questions) {
            // 选择题：对选项打乱顺序，并根据原答案获取乱序后的答案
            if(question.getType() == QuestionTypeEnum.CHOICE.getCode() || question.getType() == QuestionTypeEnum.MULTIPLE.getCode()){
                Collections.shuffle(question.getOptions());
            }
        }
        // 乱序后按照类型排序，这样打乱顺序并相同类题目在一起
        Collections.shuffle(questions);
        // 按照题型type排序
        questions.sort(Comparator.comparing(PaperQuestionVo::getType));
        paperDetailedVo.setQuestions(questions);



        // 生成记录并入库保存
        PaperLog paperLog = new PaperLog();
        paperLog.setUserType(FrontThreadLocal.getUserType());
        paperLog.setUserId(FrontThreadLocal.getUserId());
        // 用户名称
        paperLog.setUserName(paperAssign.getUserName());

        paperLog.setPaperId(paperDetailedVo.getId());
        paperLog.setPaperTitle(paperDetailedVo.getTitle());
        paperLog.setTotalScore(paperDetailedVo.getScore());
        paperLog.setPassingMark(paperDetailedVo.getPassingMark());
        paperLog.setCreateTime(System.currentTimeMillis()/1000);
        paperLog.setUpdateTime(System.currentTimeMillis()/1000);
        paperLog.setCreateIp(IpUtils.getIpAddress());
        paperLog.setQuestions(questions);
        paperLog.setCredit(paperDetailedVo.getCredit());
        paperLogMapper.insert(paperLog);

        // 将正确答案的标记全部设置为null
        for(PaperQuestionVo questionVo: paperLog.getQuestions()){
            if(questionVo.getType() == QuestionTypeEnum.CHOICE.getCode() || questionVo.getType() == QuestionTypeEnum.MULTIPLE.getCode()) {
                for(QuestionOption option: questionVo.getOptions()){
                    option.setCorrect(null);
                }
            }
        }

        paperDetailedVo.setLogId(paperLog.getId());
        return paperDetailedVo;
    }


    /**
     * 交卷
     */
    @Override
    public void submit(SubmitValidate submitValidate) {
        PaperLog paperLog = paperLogMapper.selectById(submitValidate.getLogId());
        Assert.notNull(paperLog, "考试记录不存在");
        if (!Objects.equals(paperLog.getUserId(), FrontThreadLocal.getUserId())) {
            throw new OperateException("非法操作");
        }
        if (paperLog.getStatus() != 0) {
            throw new OperateException("请勿重复交卷");
        }
        // 将用户提交的答案转成map方便根据题目ID获取答案
        Map<Integer, String> answerMap = new HashMap<>();
        for (PaperQuestionVo submitQuestionVo : submitValidate.getQuestions()) {
            // 如果是语音题则保存url
            if(submitQuestionVo.getType() == QuestionTypeEnum.VOICE.getCode()){
                answerMap.put( submitQuestionVo.getId(), submitQuestionVo.getUrl());
            }else{
                answerMap.put(submitQuestionVo.getId(), submitQuestionVo.getUserAnswer());
            }
        }

        // 将用户答案保存起来
        for (PaperQuestionVo questionVo : paperLog.getQuestions()) {
            if(questionVo.getType() == QuestionTypeEnum.VOICE.getCode()) {
                questionVo.setUrl(answerMap.getOrDefault(questionVo.getId(), ""));
            }else questionVo.setUserAnswer(answerMap.getOrDefault(questionVo.getId(), ""));
        }
        // 标记为判卷中
        paperLog.setStatus(PaperAssignStatusEnum.JUDGING.getCode());
        paperLog.setSubmitIp(IpUtils.getIpAddress());
        paperLog.setSubmitTime(System.currentTimeMillis() / 1000);
        paperLog.setUpdateTime(System.currentTimeMillis() / 1000);
        // 计算花费时间
        long time = paperLog.getUpdateTime() - paperLog.getCreateTime();
        paperLog.setTime((int) time);
        paperLogMapper.updateById(paperLog);

        PaperAssign paperAssign = paperAssignMapper.selectOne(new QueryWrapper<PaperAssign>()
                .eq("paper_id", paperLog.getPaperId())
                .eq("user_id", paperLog.getUserId())
                .in("status", PaperAssignStatusEnum.WAIT.getCode(), PaperAssignStatusEnum.FAILED.getCode())
                .and(i -> i.eq("days", 0)
                        .or().apply("create_time + days * 86400 > " + System.currentTimeMillis() / 1000))
                .eq("is_delete", 0)
                .last("limit 1"));
        if (paperAssign != null) {
            paperAssign.setTimes(paperAssign.getTimes() + 1);
            paperAssign.setStatus(paperLog.getStatus());
            paperAssign.setUpdateTime(System.currentTimeMillis() / 1000);
            paperAssignMapper.updateById(paperAssign);
        }

        new Thread(() -> {
            _judge(paperLog);
        }).start();
    }

    private void _judge(PaperLog paperLog) {
        // 计算分数
        int score = 0;
        int correctCount = 0;
        int wrongCount = 0;
        for(PaperQuestionVo questionVo: paperLog.getQuestions()){
            // 单选，多选，判断题
            if(
                    questionVo.getType() == QuestionTypeEnum.CHOICE.getCode()
                    || questionVo.getType() == QuestionTypeEnum.MULTIPLE.getCode()
                    || questionVo.getType() == QuestionTypeEnum.JUDGEMENT.getCode()
            ) {

                // 提取标准答案
                List<String> answers = new ArrayList<>();
                int index = 65;
                for(QuestionOption option: questionVo.getOptions()){
                    if(option.getCorrect() != null && option.getCorrect()){
                        answers.add(String.valueOf((char)index));
                    }
                    index ++;
                }
                String[] userAnswers = null;
                if(questionVo.getUserAnswer() != null){
                    userAnswers = questionVo.getUserAnswer().split(",");
                    Arrays.sort(userAnswers);
                }

                if (Arrays.equals(answers.toArray(), userAnswers)) {
                    score += questionVo.getScore();
                    correctCount++;
                    questionVo.setResult(1);
                }else {
                    wrongCount++;
                    questionVo.setResult(0);
                }
            }
            // 简答或者语音题
            else if(questionVo.getType() == QuestionTypeEnum.TEXT.getCode() || questionVo.getType() == QuestionTypeEnum.VOICE.getCode()){
                if(questionVo.getType() == QuestionTypeEnum.VOICE.getCode() && questionVo.getUrl() != null){
                    RpcResult<String> textResult = collegeRpc.voice2text(questionVo.getUrl());
                    if(textResult != null && textResult.getData() != null){
                        questionVo.setUserAnswer(textResult.getData());
                    }
                }
                String userAnswer = questionVo.getUserAnswer();
                if(userAnswer != null){
                    boolean hasKeyword = false;
                    List<String> keywords = ListUtils.stringToListAsStr(questionVo.getKeywords(), ",");
                    for(String keyword: keywords){
                        if(userAnswer.contains(keyword)){
                            hasKeyword = true;
                            break;
                        }
                    }
                    if(hasKeyword) {
                        score += questionVo.getScore();
                        correctCount++;
                        questionVo.setResult(1);
                    }else{
                        wrongCount++;
                        questionVo.setResult(0);
                    }
                }else {
                    wrongCount++;
                    questionVo.setResult(0);
                }
            }
        }
        paperLog.setScore(score);
        // 判断是否通过
        if(score >= paperLog.getPassingMark()) {
            paperLog.setStatus(PaperAssignStatusEnum.PASSED.getCode());
        }else{
            paperLog.setStatus(PaperAssignStatusEnum.FAILED.getCode());
        }
        paperLog.setUpdateTime(System.currentTimeMillis()/1000);
        paperLog.setCorrectCount(correctCount);
        paperLog.setWrongCount(wrongCount);
        paperLogMapper.updateById(paperLog);

        // 更新对应的考试指派任务
        PaperAssign paperAssign = paperAssignMapper.selectOne(new QueryWrapper<PaperAssign>()
                .eq("paper_id", paperLog.getPaperId())
                .eq("user_id", paperLog.getUserId())
                .eq("status", PaperAssignStatusEnum.JUDGING.getCode())
                .eq("is_delete", 0)
                .last("limit 1"));
        if(paperAssign != null){
            if(paperAssign.getHighest() == null || paperLog.getScore() > paperAssign.getHighest()){
                paperAssign.setHighest(paperLog.getScore());
            }
            paperAssign.setStatus(paperLog.getStatus());
            paperAssign.setUpdateTime(System.currentTimeMillis()/1000);
            paperAssignMapper.updateById(paperAssign);
        }

        // 通过后发放课程学分
        if(paperLog.getStatus() == PaperAssignStatusEnum.PASSED.getCode()){
            creditMapper.add(paperLog.getUserId(), paperLog.getPaperId(), paperLog.getCredit(), CreditActionEnum.INC_PASS, "考试通过");
        }
    }

    /**
     * 考试记录
     */
    @Override
    public List<PaperLogListedVo> record() {
        String userId = FrontThreadLocal.getUserId();
        List<PaperLog> paperLogs = paperLogMapper.selectList(new QueryWrapper<PaperLog>()
                .eq("user_id", userId)
                // 不返回未交卷的数据
                .gt("status", 0)
                .orderByDesc("create_time"));
        List<PaperLogListedVo> voList = new ArrayList<>();
        for(PaperLog paperLog: paperLogs){
            PaperLogListedVo vo = new PaperLogListedVo();
            BeanUtils.copyProperties(paperLog, vo);
            vo.setTitle(paperLog.getPaperTitle());
            vo.setStatusName(PaperAssignStatusEnum.getMsgByCode(paperLog.getStatus()));
            vo.setCreateTime(TimeUtils.timestampToDate(paperLog.getCreateTime()));
            voList.add(vo);
        }
        return voList;
    }

    /**
     * 考试记录详情
     */
    @Override
    public PaperLogDetailedVo recordDetail(Integer logId) {
        PaperLog paperLog = paperLogMapper.selectById(logId);
        Assert.notNull(paperLog, "考试记录不存在");
        Assert.isTrue(Objects.equals(paperLog.getUserId(), FrontThreadLocal.getUserId()), "非法操作");
        PaperLogDetailedVo vo = new PaperLogDetailedVo();
        BeanUtils.copyProperties(paperLog, vo);
        vo.setStatusName(PaperAssignStatusEnum.getMsgByCode(paperLog.getStatus()));
        // 将时间秒转成时:分:秒格式
        vo.setTime(TimeUtils.secondToTime(paperLog.getTime()));
        return vo;
    }
}
